/* Copyright (c) 2001 Nextel Communications, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   - Neither the name of Nextel Communications, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NEXTEL
 * COMMUNICATIONS, INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.nextel.ui; // Generated package na

import com.nextel.util.Debug;
import com.nextel.util.Logger;
import pe.com.nextel.util.ArrayList;
import pe.com.nextel.util.List;

class OFocusManager {
    // initial size of the vector of focusable components

//    private final static int INITIAL_SIZE = 6;
    private List focusableComponents;
    private OCompositeScreen screen;
//    private int bodyBegin;
//    private int bodyEnd;
    // The component which currently has focus
    private int focusedComponentIndex = 0;
    private OFocusableComponent focusedComponent = null;

    /**
     * Creates a new <code>OFocusManager</code> instance.
     * <p>
     * <b>This constructor should only be called AFTER all components have been
     * added to the screen.  The object created by this
     * constructor will not manage focus for any components added to the screen
     * AFTER this constructor is called</b>
     *
     * @param container The container for which this object is to manage focus.
     */
    public OFocusManager(OCompositeScreen screen) {
        if (Debug.ON) {
            Logger.dev("OFocusManager.ctor.1 ENTERED");
        }

        this.screen = screen;
//        bodyBegin = screen.getBodyRow();
//        bodyEnd = bodyBegin + screen.getBodyHeight();

        // populate a vector of components which can have focus, which may be a
        // subset of the components in the screen's container
        OComponent[] components = ((OContainer) screen).getComponents();
        for (int idx = 0; components != null && idx < components.length; idx++) {
            OComponent component = components[idx];
            if (!(component instanceof OContainer)) {
                if (component instanceof OFocusableComponent) {
                    if (focusableComponents == null) {
                        focusableComponents = new ArrayList();
                    }
                    focusableComponents.add(component);
                }
            } else // component is itself a container, so we need to add its focusable
            {    // components to the vector of all focusable components=
                OComponent[] subComponents =
                        ((OContainer) component).getComponents();
                for (int idx2 = 0; subComponents != null && idx2 < subComponents.length; idx2++) {
                    OComponent subComponent = subComponents[idx2];
                    if (subComponent instanceof OFocusableComponent) {
                        if (focusableComponents == null) {
                            focusableComponents = new ArrayList();
                        }
                        focusableComponents.add(subComponent);

                    }
                }
                subComponents = null;
            }
        }
        components = null;
        // set the focused component to the first component
        if (focusableComponents != null && focusableComponents.size() > 0) {
            focusedComponent =
                    (OFocusableComponent) focusableComponents.get(focusedComponentIndex);
            focusedComponent.setFocus(OFocusEvent.FOCUS_GAINED);
        }
        //aqui se comento
//        System.gc();
        if (Debug.ON) {
            Logger.dev("OFocusManager.ctor.1 EXITTING");
        }
    } // constructor

    public OFocusManager(OCompositeScreen screen, int indfocus) {
        focusedComponentIndex = indfocus;

        if (Debug.ON) {
            Logger.dev("OFocusManager.ctor.1 ENTERED");
        }

        this.screen = screen;
//        bodyBegin = screen.getBodyRow();
//        bodyEnd = bodyBegin + screen.getBodyHeight();

        // populate a vector of components which can have focus, which may be a
        // subset of the components in the screen's container
        OComponent[] components = ((OContainer) screen).getComponents();
        for (int idx = 0; components != null && idx < components.length; idx++) {
            OComponent component = components[idx];
            if (!(component instanceof OContainer)) {
                if (component instanceof OFocusableComponent) {
                    if (focusableComponents == null) {
                        focusableComponents = new ArrayList();
                    }
                    focusableComponents.add(component);
                }
            } else // component is itself a container, so we need to add its focusable
            {    // components to the vector of all focusable components=
                OComponent[] subComponents =
                        ((OContainer) component).getComponents();
                for (int idx2 = 0; subComponents != null && idx2 < subComponents.length; idx2++) {
                    OComponent subComponent = subComponents[idx2];
                    if (subComponent instanceof OFocusableComponent) {
                        if (focusableComponents == null) {
                            focusableComponents = new ArrayList();
                        }
                        focusableComponents.add(subComponent);
                    }
                }
                subComponents = null;
            }
        }
        components = null;
        // set the focused component to the first component
        if (focusableComponents != null && focusableComponents.size() > 0) {
            focusedComponent =
                    (OFocusableComponent) focusableComponents.get(focusedComponentIndex);
            focusedComponent.setFocus(OFocusEvent.FOCUS_GAINED);
        }
        //aqui se comento
//        System.gc();
        if (Debug.ON) {
            Logger.dev("OFocusManager.ctor.1 EXITTING");
        }
    } // constructor

    /**
     * Processes a key press.
     * <p>
     * if the key pressed is the right or left key focus is changed appropriately.
     * All other keys are passed to the component which currently has focus.
     *
     * @param keyCode Code of the key pressed.
     */
    public void keyPressed(int keyCode) {
//
//        System.out.println("OFocusManager.keyPressed CALLED w/keyCode=" + keyCode);
        //
        // NOTE: Not all screens will have a focusedComponent
        //       therefore we shall check for null and ensure
        //       it can handle the keypress.  Otherwise, we
        //       shall get the traverse directions
        //
//        System.out.println("focusedComponent=" + focusedComponent);
        if (focusedComponent == null) {

            //TODO agregado by prueba
            moveNextFieldNoFocus(keyCode);


        } else {
            int directions = focusedComponent.getTraverseDirections();

//            System.out.println("directions=" + directions);

            if (keyCode == OAbstractScreen.RIGHT_KEY &&
                    (OFocusableComponent.RIGHT & directions) > 0) {
                moveToNextField();
            } else if (keyCode == OAbstractScreen.LEFT_KEY &&
                    (OFocusableComponent.LEFT & directions) > 0) {
                moveToPreviousField();
            } else if (keyCode == OAbstractScreen.DOWN_KEY &&
                    (OFocusableComponent.DOWN & directions) > 0) {
                moveToNextField();
            } else if (keyCode == OAbstractScreen.UP_KEY &&
                    (OFocusableComponent.UP & directions) > 0) {
                moveToPreviousField();
            } else // pass the key stroke to the component with focus
            {
                focusedComponent.keyPressed(keyCode);
            }
        }

//        if (Debug.ON) {
//            Logger.dev("OFocusManager.keyPressed EXITTING");
//        }
    } // keyPressed

    /**
     * Processes a right key press.  if the current component is not
     * the last component in the container then focus is passed to the component
     * of rank 1 greater than the current component.
     *
     */
    protected void moveToNextField() {
//        if (Debug.ON) {
//        System.out.println("OFocusManager.processRightKeyPRessed CALLED ");
//        }
        if (focusableComponents != null && focusedComponentIndex < focusableComponents.size() - 1) {
            // not the last component, so find the next focusable component
            OFocusableComponent nextComponent =
                    (OFocusableComponent) focusableComponents.get(++focusedComponentIndex);
            focusedNoFocusableComponent = null;
            moveFocus((OFocusableComponent) nextComponent);
        } else { // last focusable component, so merely beep
            if (focusedNoFocusableComponent == null && focusableComponents != null) {
                OFocusableComponent previousComponent = (OFocusableComponent) focusableComponents.get(focusedComponentIndex);
                focusedNoFocusableComponent = screen.getNextComponent(previousComponent);
//                System.out.println("the screen.moveToNextField(previousComponent) " + focusedNoFocusableComponent);
                if (focusedNoFocusableComponent != null) {
                    screen.adjustBodyOffset(focusedNoFocusableComponent);
                }
            } else {
                focusedNoFocusableComponent = screen.getNextComponent(focusedNoFocusableComponent);
//                System.out.println("the screen.moveToNextField(previousComponent) " + focusedNoFocusableComponent);
                if (focusedNoFocusableComponent == null) {
                    OHandset.beep();
                } else {
                    screen.adjustBodyOffset(focusedNoFocusableComponent);
                }
            }
        }
//        if (Debug.ON) {
//            Logger.dev("OFocusManager.processRightKeyPress EXITTING ");
//        }
    } // moveToNextField

    /**
     * Processes a left key press.  if the current component is not the first
     * component in the container then focus is passed to the component of rank
     * 1 less than the current component
     *
     */
    protected void moveToPreviousField() {
//        if (Debug.ON) {
//        System.out.println("OFocusManager.processLeftKeyPress CALLED ");
//        }
        if (focusedComponentIndex > 0 && focusableComponents != null) {
            OFocusableComponent previousComponent =
                    (OFocusableComponent) focusableComponents.get(--focusedComponentIndex);
            focusedNoFocusableComponent = null;
            moveFocus((OFocusableComponent) previousComponent);
        } else // first component, so merely beep
        {
            if (focusedNoFocusableComponent == null && focusableComponents != null) {
                OFocusableComponent previousComponent = (OFocusableComponent) focusableComponents.get(0);
                focusedNoFocusableComponent = screen.getPreviousComponent(previousComponent);
//                System.out.println("the screen.getPreviousComponent(previousComponent) " + focusedNoFocusableComponent);
                if (focusedNoFocusableComponent == null) {
                    OHandset.beep();
                } else {
                    screen.adjustBodyOffset(focusedNoFocusableComponent);
                }
            } else {
                focusedNoFocusableComponent = screen.getPreviousComponent(focusedNoFocusableComponent);
//                System.out.println("the screen.getPreviousComponent(previousComponent) " + focusedNoFocusableComponent);
                if (focusedNoFocusableComponent == null) {
                    OHandset.beep();
                } else {
                    screen.adjustBodyOffset(focusedNoFocusableComponent);
                }
            }
        }
//        if (Debug.ON) {
//            Logger.dev("OFocusManager.processLeftKeyPress EXITTING ");
//        }


    } // moveToPreviousField
    private OComponent focusedNoFocusableComponent;

    /**
     * Moves focus to the first component in the next row.
     *
     */
    protected void moveToNextRow() {
        if (focusableComponents != null) {

            int nextRank = focusedComponentIndex + 1;
            boolean moved = false;
            OFocusableComponent gainer = null;
            int lastRank = focusableComponents.size() - 1;
            if (nextRank <= lastRank) { // there are more components
                int currentY = focusedComponent.getY();
                boolean keepLooking = true;
                for (; keepLooking && nextRank <= lastRank; nextRank++) {
                    OFocusableComponent nextComponent =
                            (OFocusableComponent) focusableComponents.get(nextRank);
                    if (nextComponent.getY() > currentY) { // component is in a later row
                        gainer = (OFocusableComponent) nextComponent;
                        focusedComponentIndex = nextRank;
                        keepLooking = false;
                        moved = true;
                    }
                }
            }
            if (moved) {
                moveFocus(gainer);
            }
        }
    } // moveToNextRow

    /**
     * Moves focus to the last component in the previous row.
     *
     */
    protected void moveToPreviousRow() {
        if (focusableComponents != null) {
            int priorRank = focusedComponentIndex - 1;
            boolean moved = false;
            OFocusableComponent gainer = null;
            int firstRank = 0;
            if (priorRank >= firstRank) { // there is a previous component
                int currentY = focusedComponent.getY();
                boolean keepLooking = true;
                for (; keepLooking && priorRank >= firstRank; priorRank--) {
                    OComponent previousComponent =
                            (OComponent) focusableComponents.get(priorRank);
                    if (previousComponent.getY() < currentY &&
                            previousComponent instanceof OFocusableComponent) {
                        gainer = (OFocusableComponent) previousComponent;
                        focusedComponentIndex = priorRank;
                        keepLooking = false;
                        moved = true;
                    }
                }
            }

            if (moved) {
                moveFocus(gainer);
            }
        }
    } // moveToPreviousRow

    /**
     * Moves the focus to the specified component.
     *
     * @param gainer The component gaining focus
     */
    protected void moveFocus(OFocusableComponent gainer) {
        if (Debug.ON) {
            Logger.dev("@@@ OFocusManager.moveFocus ENTERED");
        }
        focusedComponent.setFocus(OFocusEvent.FOCUS_LOST);
        gainer.setFocus(OFocusEvent.FOCUS_GAINED);
        focusedComponent = gainer;
        screen.adjustBodyOffset(gainer);
        if (Debug.ON) {
            Logger.dev("OFocusManager.moveFocus EXITTING");
        }
    } // moveFocus

    /**
     * Gets the component that has focus.
     *
     * @return The component that has focus.
     */
    OFocusableComponent getFocused() {
        return focusedComponent;
    } // getFocused

    public void clearAll() {
//        System.out.println("OFocusManager");
        if (focusableComponents != null) {
            focusableComponents.clear();
            focusableComponents = null;
        }

        screen = null;

        focusedComponent = null;

//        System.gc();
    }

    private void moveNextFieldNoFocus(int keyCode) {
//        System.out.println("moveNextFieldNoFocus.keyCode" + keyCode);
//        System.out.println("screen.getOContainerHelper()=" + screen.getOContainerHelper());
        if (screen.getOContainerHelper() != null && screen.getComponentCount() > 0) {
            screen.getOContainerHelper().keyPressed(keyCode);
        }
    }

    public int getFocusedComponentIndex() {
        return focusedComponentIndex;
    }
}// OFocusManager

